
#include <stdio.h>
#include "stdlib.h"
#include <conio.h>
#include <windows.h>

#define X 40
#define Y 40

    typedef struct SnakeBody SnakeBody;
struct SnakeBody
{
    struct SnakeBody *next;
    int x;
    int y;
};

/* createMap();
 * setSnake();                 -----init()?
 *
 * -Snake:
 *      add();
 *      pop();
 *      clear();
 *
 * mainLoop:
 *     ------init();
 *     loop:
 *         render();
 *         interactive();
 *         collisionDetect(); -> gameOver();->reStart();
 *     Vars:
 *     int map[x][y] 0 empty; 1 wall; -1 food;
 *     int tmpMap 0 empty; 1 wall; -1 food; 2 head; 3 body; 4 tail;
 *     SnakeBody *head;
 *     int usrInput;
 */
void *memcpy_handle_overlap(void *dest, void *src, unsigned int count);

void render(struct SnakeBody *head, int map[X][Y]);

void interactive(int *usrInput);

void createMap(int map[X][Y]);

void setSnake(struct SnakeBody *head, int map[X][Y]);

void setFood(int map[X][Y]);

int collisionDetect(struct SnakeBody *head, int map[X][Y], int *score);

void mainLoop();

void gameOver(int score, struct SnakeBody *head);

void add(struct SnakeBody *head, int x, int y);

void pop(struct SnakeBody *head);

void clear(struct SnakeBody *head);

int main()
{
    mainLoop();
    return 0;
}

void mainLoop()
{
    int score = 0;
    int vx = 0, vy = 0;
    int map[X][Y] = {0};
    createMap(map);
    struct SnakeBody sh = {NULL, 0, 0};
    struct SnakeBody *head = &sh;
    setSnake(head, map);
    int usrInput = 0;
    while (usrInput != 27)
    {
        usrInput = 0;
        render(head->next, map);
        interactive(&usrInput);
        if (usrInput == 'a')
            vy--;
        if (usrInput == 's')
            vx++;
        if (usrInput == 'd')
            vy++;
        if (usrInput == 'w')
            vx--;
        //printf("%d\n", usrInput);
        add(head, ((head->next->x + vx) % X + X) % X, ((head->next->y + vy) % Y + Y) % Y);
        if (collisionDetect(head->next, map, &score))
            gameOver(score, head);
        Sleep(200);
    }
}

void add(struct SnakeBody *head, int x, int y)
{
    //    while (head->next != NULL) {
    //        head = head->next;
    //    }
    struct SnakeBody *body = (SnakeBody *)malloc((int)sizeof(SnakeBody));
    body->next = head->next;
    body->x = x;
    body->y = y;
    head->next = body;
}

void pop(struct SnakeBody *head)
{
    while (head->next->next != NULL)
    {
        head = head->next;
    }
    free(head->next);
    head->next = NULL;
}

void interactive(int *usrInput)
{
    if (!kbhit())
        return;
    *usrInput = getch();
}

int collisionDetect(struct SnakeBody *head, int map[X][Y], int *score)
{
    // only need to check snake's head.
    if (head->x > X || head->x < -1 || head->y > Y || head->y < -1)
        return 1;
    if (map[head->x][head->y] > 0)
        return 1;
    else if (map[head->x][head->y] == -1)
    {
        (*score)++;
        map[head->x][head->y] = 0;
        setFood(map);
        return 0;
    }
    pop(head);
    return 0;
}

void gameOver(int score, struct SnakeBody *head)
{
    // print score and free the snake.
    system("cls");
    printf("Your score is %d", score);
    Sleep(2000);
    clear(head);
}

void clear(struct SnakeBody *head)
{
    if (head->next != NULL)
        clear(head->next);
    free(head);
}

void render(struct SnakeBody *head, int map[X][Y])
{
    int tmpMap[X][Y];
    // copy the map
    //memcpy_handle_overlap(tmpMap,map,(int)sizeof(map)*X*Y);
    for (int x = 0; x < X; x++)
    {
        for (int y = 0; y < Y; y++)
        {
            tmpMap[x][y] = map[x][y];
        }
    }

    tmpMap[head->x][head->y] = 2; // mark head
    head = head->next;
    while (head->next != NULL)
    {
        tmpMap[head->x][head->y] = 3;
        head = head->next;
    }
    tmpMap[head->x][head->y] = 4;
    // print!
    // 0 empty; 1 wall; -1 food; 2 head; 3 body; 4 tail;
    system("cls");
    //    int c;
    //    for (int x = 0; x < X; x++) {
    //        for (int y = 0; y < Y; y++) {
    //            c = tmpMap[x][y];
    //            putchar(c == 0 ? ' ' : c == 1 ? '#' : c == -1 ? '*' : c == 2 ? '@' : c == 3 ? 'o' : '.');
    //        }
    //        putchar('\n');
    //    }
    char line[Y + 2] = {0};
    for (int x = 0; x < X; x++)
    {
        for (int y = 0; y < Y; y++)
        {
            int c = tmpMap[x][y];
            line[y] = (char)(c == 0) ? ' ' : ((c == 1) ? '#' : ((c == -1) ? '$' : ((c == 2) ? '@' : ((c == 3) ? 'o' : '.'))));
        }
        line[Y] = '\0';
        printf("%s\n", line);
    }
}

void *memcpy_handle_overlap(void *dest, void *src, unsigned int count)
{
    if ((NULL == dest) || (NULL == src))
        return NULL;
    char *d = (char *)dest;
    char *s = (char *)src;
    //Check for overlapping buffers:
    if ((d <= s) || (d >= s + count))
    {
        //Do normal (Upwards) Copy
        while (count-- > 0)
            *d++ = *s++;
    }
    else
    {
        //Do Downwards Copy to avoid propagation
        while (count > 0)
        {
            *(d + count - 1) = *(s + count - 1);
            --count;
        }
    }
    return dest;
}

void createMap(int map[X][Y])
{
    for (int i = 0; i < 20; i++)
        map[rand() % X][rand() % Y] = 1;
    setFood(map);
}

void setFood(int map[X][Y])
{
    int rx, ry;
    rx = rand() % X;
    ry = rand() % Y;
    while (map[rx][ry])
    {
        rx = rand() % X;
        ry = rand() % Y;
    }
    map[rx][ry] = -1;
}

void setSnake(struct SnakeBody *head, int map[X][Y])
{
    int rx, ry;
    rx = rand() % X;
    ry = rand() % Y;
    while (map[rx][ry] || map[rx - 1][ry] || map[rx - 2][ry])
    {
        rx = rand() % X;
        ry = rand() % Y;
    }
    add(head, rx - 2, ry);
    add(head, rx - 1, ry);
    add(head, rx, ry);
}